using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Text;
using System.Text.RegularExpressions;

namespace cmdPhone
{
    /// <summary>
    /// Class deals with user commands and invokes the appropriate method in the Phone class.
    /// </summary>
    class InputReader
    {
        Phone phone;

        public InputReader()
        {
            phone = new Phone();
        }

        /// <summary>
        /// Takes user commands, interprets them and calls the appropriate method
        /// </summary>
        /// <param name="args">User's commands/parameters</param>
        public void readInput(string[] args)
        {
            if (args.Length == 1)
            {

                switch (args[0])
                {
                    case "/?":
                    case "?":
                    case "help":
                        showHelp();
                        break;

                    case "log":
                    case "login":
                        loginWithNoParameters();
                        break;

                    case "password":
                    case "changepassword":
                    case "updatepassword":
                    case "updatepass":
                    case "pass":
                        changePasswordNoParameters();
                        break;

                    case "account":
                    case "create":
                    case "createaccount":
                        createAccountNoParameters();
                        break;

                    case "sms":
                    case "text":
                        smsWithNoParameters();
                        break;

                    case "insms":
                    case "inbound":
                    case "in":
                    case "inboundsms":
                        inboundSMSHelper();
                        break;
    
                    case "": //Return with no command
                        break;

                    case "setup":
                        SetupWizzard.web21cSetupWizzard();
                        break;

                    default:
                        invalidCommand();
                        break;
                }
            }

            else
            {
                switch (args[0])
                {
                    case "call":
                        voiceCall(args);
                        break;

                    case "conf":
                    case "conference":
                        conferenceCall(args);
                        break;

                    case "sms":
                    case "text":
                        sendSMS(args);
                        break;

                    case "insms":
                    case "inbound":
                    case "in":
                    case "inboundsms":
                        inboundSMS(args);
                        break;

                    case "flush":
                        flushInboundSMS(args);
                        break;

                    case "log":
                    case "login":
                    case "logon":
                        login(args);
                        break;

                    case "password":
                    case "changepassword":
                    case "updatepassword":
                    case "updatepass":
                    case "pass":
                    case "passwd":
                        changePassword(args);
                        break;

                    case "account":
                    case "create":
                    case "createaccount":
                        createAccount(args);
                        break;

                    default:
                        break;
                }
            }

        }


        #region White Label Authentication(WLA) related methods (WLAuthentication is no longer required for calling any of the services)

        /// <summary>
        /// Logs user into White Label Authentication
        /// This used to be required for Phone Location, IAM and Contacts. 
        /// Unfortunately these capabilities have been withdrawn from the latest version of the BT Web21c SDK thus
        /// Making all the WLA related methods (login, Password, Account, etc) somewhat useless/irrelevant.
        /// </summary>
        /// <param name="args">0="login", 1=uid, 2=password</param>
        private void login(string[] args)
        {
            //args = verifyArgs(args);

            bool status;
            if (args.Length == 2)
            {
                Interaction.output(Resources.prompt_user_for_password);
                string password = Console.ReadLine();
                status = phone.login(args[1], password);

            }
            else if (args.Length == 3)
            {
                Interaction.output(Resources.say_logging_in);
                status = phone.login(args[1], args[2]);
            }
            else
            {
                status = false;
                invalidCommand();
            }

            //Providing feedback about successful loggin attempt
            if (status)
            {
                Interaction.output(Resources.say_logon_has_been_successful);
            }
        }



        /// <summary>
        /// Helper method to deal with special case of login with no parameters
        /// </summary>
        private void loginWithNoParameters()
        {
            Interaction.output(Resources.prompt_user_for_userid);
            string uid = Console.ReadLine();
            Interaction.output(Resources.prompt_user_for_password);
            string password = Console.ReadLine();

            string[] args = { "login", uid, password };
            login(args);
        }



        /// <summary>
        /// Creates a WLA Account
        /// </summary>
        /// <param name="args">0="createAccount" 1=email</param>
        private void createAccount(string[] args)
        {
            if (args.Length == 2)
            {
                bool status = phone.createAccount(args[1]);

                if (status)
                {
                    Interaction.output(Resources.email_sent_with_password);
                }

            }
            else
            {
                invalidCommand();
            }

        }



        /// <summary>
        /// Helper method to create WLA accounts when no parameters are passed
        /// </summary>
        private void createAccountNoParameters()
        {
            Interaction.output(Resources.prompt_for_desired_wla_account_name);
            string account = Console.ReadLine();
            string[] args = { "createAccount", account };
            createAccount(args);

        }



        /// <summary>
        /// Changes password for a WLA account
        /// </summary>
        /// <param name="args">0="password" 1=email 2=password 3=newPassword</param>
        private void changePassword(string[] args)
        {
            if (args.Length == 4)
            {
                bool status = phone.changePassword(args[1], args[2], args[3]);

                if (status)
                {
                    Interaction.output(Resources.password_changed_ok);
                }
            }
            else
            {
                invalidCommand();
            }

        }


        /// <summary>
        /// Helper method to deal with changePassword SMS no parameters
        /// </summary>
        private void changePasswordNoParameters()
        {
            Interaction.output(Resources.prompt_user_for_userid);
            string uid = Console.ReadLine();
            Interaction.output(Resources.prompt_user_for_password);
            string current_password = Console.ReadLine();
            Interaction.output(Resources.prompt_user_new_password);
            string password1 = Console.ReadLine();
            Interaction.output(Resources.prompt_user_retype_new_password);
            string password2 = Console.ReadLine();

            if (password1 != password2)
            {
                Interaction.output(Resources.passwords_do_not_match);
                return;
            }
            else
            {
                string[] args2 = { "password", uid, current_password, password1 };
                changePassword(args2);
            }

        }


        #endregion WLA related methods


        /// <summary>
        /// Reads inbound SMSs with the provided keyword
        /// </summary>
        /// <param name="args">0="inbound" 1=key</param>
        private void inboundSMS(string[] args)
        {
            if (args.Length==2)
            {
                Collection<BT.Sdk.Messaging.InboundCapability.Message> inTexts = phone.readInboundSMS(args[1]);

                if (inTexts.Count == 0)
                {
                    Interaction.output(Resources.no_sms_messages_on_server);
                }
                else 
                {
                    foreach (BT.Sdk.Messaging.InboundCapability.Message text in inTexts)
                    {
                        Interaction.output(text.SenderUri);
                        Interaction.output(text.MessageText);
                        Interaction.output(text.MessageId);
                        Interaction.output(text.MessageText);
                    }

                }
                
            }
            else
            {
                invalidCommand();
            }
            
        }


        /// <summary>
        /// Helper method to deal with inbound SMS no parameters
        /// </summary>
        private void inboundSMSHelper()
        {
            Interaction.output(Resources.prompt_for_inbound_sms_keyword);
            string key = Interaction.input(); ;
            string[] args = { "inbound", key };
            inboundSMS(args);
        }



        /// <summary>
        /// Deletes the SMS messages stored on the server.
        /// </summary>
        /// <param name="args">0="flush" 1=keyword</param>
        private void flushInboundSMS(string[] args)
        {
            if (args.Length == 2)
            {
                bool status = phone.flushInboundSMSs(args[1]);
                if (status)
                {
                    Interaction.output(Resources.inbound_sms_flushed_ok);
                }

            }
            else
            {
                invalidCommand();
            }

        }



        /// <summary>
        /// Connects two phones together
        /// </summary>
        /// <param name="args">0="call" 1=caller 2=callee</param>
        private void voiceCall(string[] args)
        {
            args = verifyArgs(args);

            if (args.Length == 3)
            {
                Interaction.output(Resources.say_starting_call);
                bool status = phone.voiceCall(args[1], args[2]);
                if (status)
                {
                    Interaction.output(Resources.say_phone_call_started_ok);
                }

            }
            else
            {
                invalidCommand();
            }

        }

        /// <summary>
        /// Connects two or more phones together
        /// </summary>
        /// <param name="args">0="conf" 1=phoneNumber 2=phoneNumber [3=phoneNumber]...</param>
        private void conferenceCall(string[] args)
        {
            args = verifyArgs(args);

            if (args.Length == 2)
            {
                invalidCommand();
            }
            else
            {
                Interaction.output(Resources.say_starting_conference_call);
                Collection<string> phones = new Collection<string>();

                //Cannot use foreach here due to the call to isValidTelNumber(ref..)
                for (int i = 1; i < args.Length; i++)
                {
                    //If not the command word (i.e. "conf" or "conference")
                    if (i != 0)
                    {
                        if (phone.isValidTelNumber(ref args[i]))
                        {
                            phones.Add(args[i]);
                        }
                        else 
                        {
                            Interaction.output(String.Format("{0} {1}", args[i], Resources.invalid_phone_number_format));
                        }
                        
                    }

                }

                bool status = phone.conferenceCall(phones);
                if (status)
                {
                    Interaction.output(Resources.say_conf_call_started_ok);
                }

            }
        }


        /// <summary>
        /// Send Outbound SMS messages
        /// </summary>
        /// <param name="args">0="sms", 1=destination, 2=message,[3=sender]</param>
        private void sendSMS(string[] args)
        {
            if (args.Length == 4 || args.Length == 3)
            {
                args = verifyArgs(args);

                Interaction.output(Resources.sending_sms_message);

                if (args.Length == 3) //Dealing with no "Sender" parameter
                {
                    string[] args2 = { args[1], args[2], Resources.default_sms_sender_name_if_non_provided };

                    args = args2;
                }

                bool messageSentStatus = phone.sendTextMessage(args[1], args[2], args[3]);

                //Notify user if SMS has been sent or not
                if (messageSentStatus)
                {
                    Interaction.output(Resources.sms_sent_ok);
                }
                else
                {
                    Interaction.output(Resources.sms_general_error);
                }

            }

            //Command/Parameters provided are invalid
            else invalidCommand();

        }

        
        /// <summary>
        /// Helper method to deal with special case of send SMS with no parameters
        /// </summary>
        private void smsWithNoParameters()
        {
            //Gathering required details from the user
            Interaction.output(Resources.prompt_for_destination_number);
            string destination = Interaction.input();

            Interaction.output(Resources.prompt_for_sms_message_content);
            string message = Interaction.input();

            Interaction.output(Resources.prompt_for_sender_name);
            string from = Interaction.input();

            //Repackege data and forward it to sendSMS method
            string[] args = { "sms", destination, message, from };
            sendSMS(args);
        }



        /// <summary>
        /// Displays help 
        /// </summary>
        private static void showHelp()
        {
            Interaction.output(Resources.display_help);

        }

        /// <summary>
        /// Let the user know that the command entered is invalid
        /// </summary>
        private static void invalidCommand()
        {
            Interaction.output(Resources.command_syntax_incorrect);
            Interaction.output(Resources.suggest_help_syntax);
        }


        /// <summary>
        /// Checks if a given string has quotes and removes them if present
        /// </summary>
        /// <param name="text">string</param>
        /// <returns>string with no quotes</returns>
        private string removeQuotes(string text)
        {
            if (!String.IsNullOrEmpty(text))
            {
                if (text.StartsWith("\"") && text.EndsWith("\""))
                {
                    return text.Substring(1, text.Length - 2);
                }
            }

            return text;

        }



        /// <summary>
        /// Sets arguments into the correct format
        /// i.e. all lower case, remove empty or null parameters, remove quotes
        /// </summary>
        /// <param name="args">array of arguments</param>
        /// <returns>correctly formatted array</returns>
        private string[] verifyArgs(string[] args)
        {
            List<string> argList = new List<string>(args);

            for (int i = 0; i < argList.Count; i++)
            {
                argList[i] = argList[i].ToLower();
                argList[i] = removeQuotes(argList[i]);

                if (String.IsNullOrEmpty(argList[i]))
                {
                    argList.RemoveAt(i);
                }

            }

            return argList.ToArray();

        }



        /// <summary>
        /// Parses commands
        /// It can interpret quotes correctly
        ///  e.g. one two three "four five six"
        ///  Would return 4 parameters:
        ///     0 - one
        ///     1 - two
        ///     2 - three
        ///     3 - four five six
        /// </summary>
        /// <param name="command">parameters separated by space</param>
        /// <returns>Parameters in array form</returns>
        public string[] getCommands(string command)
        {
            if (string.IsNullOrEmpty(command))
            {
                return command.Split(' ');
            }
            else
            {
                Regex regex = new Regex(@"((\s*(""(?<param>.+?)""|(?<param>\S+))))",
                RegexOptions.ExplicitCapture);
                MatchCollection matches = regex.Matches(command);
                string[] interpretedParameters;

                interpretedParameters = new string[matches.Count];
                for (int i = 0; i < matches.Count; i++)
                    interpretedParameters[i] = matches[i].Groups["param"].Value;

                return interpretedParameters;
            }
            
        }

    }

}
